// <copyright file="AuthorisationPolicyProvider.cs" company="SharpSTS">
// Copyright (c) 2007, 2008 All Right Reserved, http://sharpsts.com/
//
// This source is subject to the Microsoft Permissive License.
// Please see the License.txt file for more information.
// All other rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// </copyright>
// <author>Barry Dorrans</author>
// <email>barryd@idunno.org</email>
// <date>2008-06-10</date>
// <summary>Contains a base, abstract class for an AuthorisationPolicyProvider</summary>

namespace SharpSTS
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Configuration.Provider;
    using System.IdentityModel.Claims;
    using System.ServiceModel;

    using Configuration;
    using Properties;
    
    /// <summary>
    /// Provides a base class for an AuthorisationPolicyProvider
    /// </summary>
    public abstract class AuthorisationPolicyProvider : ProviderBase, IAuthorisationPolicyProvider
    {
        /// <summary>
        /// Provides a sync object to lock on when building the display names
        /// </summary>
        private static readonly object syncLock = new object();
        
        /// <summary>
        /// Provides a lookup dictionary for display names for well known claim types.
        /// </summary>
        private static Dictionary<string, string> displayNames;

        #region IAuthorisationPolicyProvider Members

        /// <summary>
        /// Provides the claim information for the listed claim and assigns the specified rights to those claims.
        /// </summary>
        /// <param name="userId">The user identifier to generation the card identifier for.</param>
        /// <param name="relyingParty">The <see cref="EndpointAddress"/> of the relying party that is requesting the claims.</param>
        /// <param name="claimTypes">A collection of claim types to provide information for.</param>
        /// <param name="rights">The rights to assign to this claim.</param>
        /// <returns>A readonly collection of filled claims.</returns>
        public abstract Collection<Claim> GetClaims(
                                                    object userId, 
                                                    EndpointAddress relyingParty,
                                                    ReadOnlyCollection<string> claimTypes, 
                                                    string rights);

        /// <summary>
        /// Returns a card identifier for the supplied username and card type.
        /// </summary>
        /// <param name="userId">The user identifier to generation the card identifier for.</param>
        /// <param name="authenticationType">The type of the authentication used for the card.</param>
        /// <returns>An identifier for an information card.</returns>
        public abstract string GetCardIdForUserId(object userId, AuthenticationType authenticationType);

        /// <summary>
        /// Returns a user identifier that the managed information card belongs to.
        /// </summary>
        /// <param name="cardId"> The card identifier.</param>
        /// <param name="authenticationType">The type of the authentication used for the card.</param>
        /// <returns>The user identifier the card belongs to.</returns>
        public abstract object GetUserIdFromCardId(string cardId, AuthenticationType authenticationType);

        /// <summary>
        /// Authenticates a username and password
        /// </summary>
        /// <param name="userName">The username to authenticate.</param>
        /// <param name="password">The password to authenticate.</param>
        /// <returns>True if athenticated, otherwise false.</returns>
        public abstract bool AuthenticateUserNamePassword(string userName, string password);

        /// <summary>
        /// Validates the association between the authenticated user for a username/password backed card
        /// and the card id used during the request.
        /// </summary>
        /// <param name="userName">User name sent during authentication.</param>
        /// <param name="cardId">Card identity of the card used to send the request.</param>
        /// <param name="cardVersion">The version number of the card presented.</param>
        /// <returns>True if the username is authorised to use the card, otherwise false.</returns>
        public abstract bool AuthorisedToUseCardId(string userName, string cardId, int cardVersion);

        /// <summary>
        /// Validates the association between the PPID for a self-issued backed card
        /// and the card id used during the request.
        /// </summary>
        /// <param name="privatePersonalIdentifier">PPID sent during authentication.</param>
        /// <param name="issuerPublicKeyHash">A hash of the public key of the issuer for the PPID claim.</param>
        /// <param name="cardId">Card identity of the card used to send the request.</param>
        /// <param name="cardVersion">The version number of the card presented.</param>
        /// <returns>True if the username is authorised to use the card, otherwise false.</returns>
        public abstract bool AuthorisedToUseCardId(
                                                   Claim privatePersonalIdentifier, 
                                                   string issuerPublicKeyHash, 
                                                   string cardId,
                                                   int cardVersion);

        /// <summary>
        /// Decides if the information card used requires a refresh.
        /// </summary>
        /// <param name="cardId">The card identifier.</param>
        /// <param name="cardVersion">The card version.</param>
        /// <returns>True if a card refresh is required, otherwise false.</returns>
        public abstract bool IsCardRefreshRequired(string cardId, int cardVersion);

        /// <summary>
        /// Gets the PPID.
        /// </summary>
        /// <param name="cardId">The card identifier for the current request</param>
        /// <param name="endpoint">The relaying party <see cref="EndpointAddress"/>.</param>
        /// <param name="selectorSuggestedPPID">The selector suggested PPID.</param>
        /// <returns>A PPID for the specified card identifier.</returns>
        /// <remarks>Implementation of this method is optional; PPID generation may be provided by a seperate <see cref="PPIDGeneratorProvider"/>.</remarks>
        public virtual string GetPPID(string cardId, EndpointAddress endpoint, string selectorSuggestedPPID)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns the display description for a given claim URI.
        /// </summary>
        /// <param name="claimType">The claim to provide the display name for.</param>
        /// <returns>
        /// The display description for the specified claim.
        /// </returns>        
        /// <remarks>Display names for standard <see cref="ClaimTypes"/> are automatically provided. Custom claim display descriptions
        /// are set by the Description element in the SupportedClaims configuration collection.</remarks>
        public string GetDisplayName(string claimType)
        {
            if (string.IsNullOrEmpty(claimType))
            {
                throw new ArgumentNullException("claimType");
            }

            if (displayNames == null)
            {
                lock (syncLock)
                {
                    if (displayNames == null)
                    {
                        // Load up the standard claim descriptions.
                        displayNames = new Dictionary<string, string> 
                                                                     {
                                                                          {
                                                                              ClaimTypes.Anonymous,
                                                                              Resources.displayNameAnonymous
                                                                          },
                                                                          {
                                                                              ClaimTypes.Authentication,
                                                                              Resources.displayNameAuthentication
                                                                          },
                                                                          {
                                                                              ClaimTypes.AuthorizationDecision,
                                                                              Resources.displayNameAuthorization
                                                                          },
                                                                          {
                                                                              ClaimTypes.Country,
                                                                              Resources.displayNameCountry
                                                                          },
                                                                          {
                                                                              ClaimTypes.DateOfBirth,
                                                                              Resources.displayNameDateOfBirth
                                                                          },
                                                                          {
                                                                              ClaimTypes.DenyOnlySid,
                                                                              Resources.displayNameDenyOnlySID
                                                                          },
                                                                          {
                                                                              ClaimTypes.Dns, Resources.displayNameDNS
                                                                          },
                                                                          {
                                                                              ClaimTypes.Email,
                                                                              Resources.displayNameEmail
                                                                          },
                                                                          {
                                                                              ClaimTypes.Gender,
                                                                              Resources.displayNameGender
                                                                          },
                                                                          {
                                                                              ClaimTypes.GivenName,
                                                                              Resources.displayNameGivenName
                                                                          },
                                                                          {
                                                                              ClaimTypes.Hash, 
                                                                              Resources.displayNameHash
                                                                          },
                                                                          {
                                                                              ClaimTypes.HomePhone,
                                                                              Resources.displayNameHomePhone
                                                                          },
                                                                          {
                                                                              ClaimTypes.Locality,
                                                                              Resources.displayNameLocality
                                                                          },
                                                                          {
                                                                              ClaimTypes.MobilePhone,
                                                                              Resources.displayNameMobileTelephone
                                                                          },
                                                                          {
                                                                              ClaimTypes.Name, Resources.displayNameName
                                                                          },
                                                                          {
                                                                              ClaimTypes.NameIdentifier,
                                                                              Resources.displayNameNameIdentifier
                                                                          },
                                                                          {
                                                                              ClaimTypes.OtherPhone,
                                                                              Resources.displayNameOtherPhone
                                                                          },
                                                                          {
                                                                              ClaimTypes.PostalCode,
                                                                              Resources.displayNamePostalCode
                                                                          },
                                                                          {
                                                                              ClaimTypes.PPID, 
                                                                              Resources.displayNamePPID
                                                                          },
                                                                          {
                                                                              ClaimTypes.Rsa, 
                                                                              Resources.displayNameRSA                                                                          
                                                                          },
                                                                          {
                                                                              ClaimTypes.Sid, 
                                                                              Resources.displayNameSID
                                                                          },
                                                                          {
                                                                              ClaimTypes.Spn, 
                                                                              Resources.displayNameSPN
                                                                          },
                                                                          {
                                                                              ClaimTypes.StateOrProvince,
                                                                              Resources.displayNameStateOrProvince
                                                                          },
                                                                          {
                                                                              ClaimTypes.StreetAddress,
                                                                              Resources.displayNameStreetAddress
                                                                          },
                                                                          {
                                                                              ClaimTypes.Surname,
                                                                              Resources.displayNameSurname
                                                                          },
                                                                          {
                                                                              ClaimTypes.System,
                                                                              Resources.displayNameSystem
                                                                          },
                                                                          {
                                                                              ClaimTypes.Thumbprint,
                                                                              Resources.displayNameThumbprint
                                                                          },
                                                                          {
                                                                              ClaimTypes.Upn, 
                                                                              Resources.displayNameUPN
                                                                          },
                                                                          {
                                                                              ClaimTypes.Uri, 
                                                                              Resources.displayNameURI
                                                                          },
                                                                          {
                                                                              ClaimTypes.Webpage,
                                                                              Resources.displayNameWebPage
                                                                          },
                                                                          {
                                                                              ClaimTypes.X500DistinguishedName,
                                                                              Resources.displayNameX500DistinguishedName
                                                                          }
                                                                      };

                        // Now iterate through the supported claims configuration section, looking for descriptions, 
                        // and overriding any of the standard claim descriptions if they exist.
                        foreach (SupportedClaim supportedClaim in STSSettings.Settings.SupportedClaims)
                        {
                            if (!displayNames.ContainsKey(supportedClaim.Uri) &&
                                !string.IsNullOrEmpty(supportedClaim.Description))
                            {
                                displayNames[supportedClaim.Uri] = supportedClaim.Description;
                            }
                        }
                    }
                }
            }

            return displayNames.ContainsKey(claimType) ? displayNames[claimType] : null;
        }

        #endregion
    }
}